import { Scene } from '@babylonjs/core/scene';
import { HardLineMaterial, IHardLineMaterialOptions } from './hard-line-material';


/**
 * 软角线材质参数对象
 * @param uScaleFactor u向（线条方向）uv缩放因数
 */
interface ISoftLineMaterialOptions extends IHardLineMaterialOptions {
    uScaleFactor?: number
}


/**
 * 软角线材质
 * @param name 名称
 * @param scene 所在场景
 * @param options 参数对象
 */
class SoftLineMaterial extends HardLineMaterial {

    private _uScaleFactor!: number;

    constructor(name: string, scene: Scene, options?: Partial<ISoftLineMaterialOptions>) {
        super(name, scene, options);
    }

    protected initializeVariable(options?: Partial<ISoftLineMaterialOptions>): void {
        super.initializeVariable(options);
        const op = options || {};
        this._uScaleFactor = op.uScaleFactor || 1;
    }

    /**
     * 获取u向uv缩放因数
     */
    public get uScaleFactor() {
        return this._uScaleFactor;
    }

    /**
     * 设置u向uv缩放因数
     */
    public set uScaleFactor(factor: number) {
        this._uScaleFactor = factor;
        this.setFloat('uScaleFactor', factor);
    }

    protected initializeUniforms(): void {
        super.initializeUniforms();
        this.options.attributes.push('prePosition', 'dirPosition');
        this.options.uniforms.push('uScaleFactor');
        this.setFloat('uScaleFactor', this._uScaleFactor);
    }
    protected initializeShader(): void {
        super.initializeShader();
        this.shaderPath.vertexSource = `
            precision highp float;
            attribute vec3 position;
            attribute vec3 nextPosition;
            attribute vec3 prePosition;
            attribute vec3 dirPosition;
            attribute float dashMark;
            attribute float offset;
            attribute float lineDistance;
            uniform mat4 worldView;
            uniform mat4 projection;
            uniform float width;
            uniform float perspectiveFactor;
            uniform int sizeAttenuation;
            uniform float depthOffset;
            uniform float uScaleFactor;
            varying float vDistance;
            varying float vDashMark;
            varying vec2 vuv;
            void main(){
                vec4 mvPosition=worldView*vec4(position,1.0);
                vec4 pmvPosition=projection*mvPosition;
                vec4 mvNextPosition=worldView*vec4(nextPosition,1.0);
                vec4 pmvNextPosition=projection*mvNextPosition;
                vec4 pmvDirPosition=projection*worldView*vec4(dirPosition,1.0);
                vec4 pmvPrePosition=projection*worldView*vec4(prePosition,1.0);
                vec3 nxtP=pmvNextPosition.xyz/abs(pmvNextPosition.w);
                vec3 curP=pmvPosition.xyz/abs(pmvPosition.w);
                vec3 dirP=pmvDirPosition.xyz/abs(pmvDirPosition.w);
                vec3 preP=pmvPrePosition.xyz/abs(pmvPrePosition.w);
                vec3 cur_nxt=nxtP-curP;
                vec3 pre_cur=curP-preP;
                vec3 cur_dir=dirP-curP;

                vec3 look=vec3(0.,0.,1.);
                vec3 dirCC=mvPosition.xyz/mvPosition.w;
                vec3 dirCN=mvNextPosition.xyz/mvNextPosition.w;
                float dotC=dot(look,dirCC);
                float dotN=dot(look,dirCN);
                if(dotC*dotN<0.){
                    if(projection[3][3]==0.0){
                        look=vec3(0.,0.,-1.);
                    }
                }

                float aspect=projection[0][0]/projection[1][1];
                vec3 tripleDir=normalize(pre_cur)+normalize(cur_dir);
                cur_nxt.y*=aspect;
                tripleDir.y*=aspect;
                vec3 gapCross=normalize(cross(look,cur_nxt));
                vec3 joinCross=normalize(cross(look,tripleDir));
                gapCross.x*=aspect;
                joinCross.x*=aspect;
                vec3 dirCross=normalize(cross(-pre_cur,cur_dir));
                float dirDot=dot(look,dirCross);

                float dWidth=width;
                if(sizeAttenuation==0){
                    dWidth*=projection[1][1];
                }
                pmvPosition.z+=depthOffset;
                vec3 offsetVec=vec3(0.);

                if(dirDot>0.0){
                    if(offset>0.0){
                        if(sizeAttenuation==0){
                            offsetVec=offset*gapCross*dWidth/perspectiveFactor;
                        }else{
                            offsetVec=offset*gapCross*dWidth/perspectiveFactor*pmvPosition.w;
                        }
                        gl_Position=vec4(vec3(pmvPosition.xyz)+offsetVec,pmvPosition.w);
                    }else{
                        if(sizeAttenuation==0){
                            offsetVec=joinCross*dWidth/perspectiveFactor;
                        }else{
                            offsetVec=joinCross*dWidth/perspectiveFactor*pmvPosition.w;
                        }
                        gl_Position=vec4(vec3(pmvPosition.xyz)-offsetVec,pmvPosition.w);
                    }
                }else{
                    if(offset>0.0){
                        if(sizeAttenuation==0){
                            offsetVec=joinCross*dWidth/perspectiveFactor;
                        }else{
                            offsetVec=joinCross*dWidth/perspectiveFactor*pmvPosition.w;
                        }
                        gl_Position=vec4(vec3(pmvPosition.xyz)+offsetVec,pmvPosition.w);
                    }else{
                        if(sizeAttenuation==0){
                            offsetVec=gapCross*dWidth/perspectiveFactor;
                        }else{
                            offsetVec=gapCross*dWidth/perspectiveFactor*pmvPosition.w;
                        }
                        gl_Position=vec4(vec3(pmvPosition.xyz)-offsetVec,pmvPosition.w);
                    }
                }
                vDistance=lineDistance;
                vuv=vec2(lineDistance*uScaleFactor,sign(offset)*0.5);
                vDashMark=dashMark;
            }
        `;
    }
}


export { SoftLineMaterial };
export type { ISoftLineMaterialOptions };